DHTMLX Documentation

Managed Drag and Drop

Existing events order

The next events are generated in the course of d-n-d process :

  1. onBeforeDrag - event occurs when drag operation is started; the event can be blocked (dhtmlxgrid 1.5.1 +);
  2. onDragIn - event occurs when an item is dragged to another potential target, the event can be blocked;
  3. onDragOut - event occurs when an item is dragged out from the potential target (dhtmlxgrid 1.5.1 +);
  4. onDrag - event occurs when an item is dragged to another target and the mouse is released, the event can be blocked;
  5. onDrop - event occurs when d-n-d operation is finished.

In the default scenario there is no need to use any of the mentioned events, because the grid will process all the operations on its own. But sometimes default behavior needs to be customized - this is the time when the mentioned events can be used.

Control drag start

A very common use case is as follows: only specified items can be dragged. This can be implemented by using onBeforeDrag event.
In xml we can mark items that can be dragged in any possible way. In the following snippet it is marked by user data:
<rows>
    <row id="1"><cell> data 2 </cell></row>
    <row id="2"><cell> data 2 </cell>
        <userdata name="drag">allow</userdata>
    </row>
</rows>
After that we can add the following code to grid initialization:
grid.attachEvent("onBeforeDrag",function(id){
    if (grid.getUserData(id,"drag")=="allow") return true;        //allow drag if user data exists
    return false;                                                            //deny drag for any other cases
}
From now on each time d-n-d is started, user data of the dragged item will be checked, and d-n-d will be allowed only if it is set in XML.
Of course it is possible to use any other kind of check here, that will return "true" to allow dragging and "false" to deny dragging.

Custom drag text

The second common use case - some custom content needs to be shown instead of the default item text.
The grid has a predefined method which converts the row ID to its drag representation, but it can be redefined with custom logic:
grid.rowToDragElement=function(id){
     //any custom logic here
     var text = "<img src='some.gif'> - "+grid.cells(id,0).getValue(); //prepare text string
     return text;
}
In the above mentioned snippet the text of the dragged element will contain a fixed image and the value of the first column of the dragged row. As you can see it is possible to use any HTML inside the dragged text, so it is rather customizable. 
In case of enabled multiselect in d-n-d, it is possible to create a drag representation which reflects not the text of the dragged items, but their number.
grid.rowToDragElement=function(id){
     //any custom logic here
     var text = grid._dragged.length + "item(s)";
     return text;
}
If you want to show the text of all the dragged items in case of d-n-d with multiselect it is possible to define the custom function as follows:
grid.rowToDragElement=function(id){
     //any custom logic here
     var text="";
     for (var i=0; i<this._dragged.length; i++)
         text += grid.cells(grid._dragged[i].idd,0).getValue() + "<br/>";
     return text;
}

Control drag landing

After the item's dragging has started, the next situation which can be managed - reaction of the drag landing on the dragged item. 
There are two common use cases:


To add some custom visualization to the dragged item's landing onDragIn and onDragOut events may be used in the following way:
var last_marker = null;
mygrid.attachEvent("onDragIn",function(sid,tid,sgrid,tgrid){
    // sid - id of dragged item , tid - id of landing item
    if (tid)                                                                     // tid may be null if landing is in grid body
        grid.setRowTextStyle(tid,"background-color:red;");    // mark current landing
    return true;                                                           // mandatory! important!
})
mygrid.attachEvent("onDragOut",function(tid){
    if (tid)
        grid.setRowTextStyle(tid,""); // clear styles which were set on the previous step
})
This snippet will be called each time when any item is dragged. It sets background color to red when the dragged item enters the borders of any possible landing, and clears background color after the item is moved outside landing's borders.
Of course, in a real application another neater visualization can be used.

To enable|disable landing - onDragIn event is just enough. For example, we have the following XML:
<rows>
    <row id="1"><cell> data 1 </cell>
        <userdata name="drag">category</userdata></row>
    <row id="2"><cell> data 2</cell>
        <userdata name="drag">category</userdata></row>
    <row id="3"><cell> data 3</cell>
        <userdata name="drag">item</userdata></row>
    <row id="4"><cell> data 4</cell>
        <userdata name="drag">item</userdata></row>
</rows>
We can implement "category" and "item" logic (i.e. item can be dropped only in category) in the following way:
grid.attachEvent("onDragIn",function(sid,tid){
     if (grid.getUserData(tid,"drag")=="item") return false; // nothing can be dropped in an item, block landing
     return true;                                                       // in any other cases - allow landing
});
To block the operation we just return "false" from onDragIn event.
The described scenario has more sense for TreeGrid, but it can be also used with some customization in case of plain Grid as well.

Control drop

After the item was dragged and dropped, the next stage of process is initiated - the item needs to be correctly inserted in its new place.
And again there is no need to do anything, but default existing logic that will process operation on its own.
But in some cases we need to customize the operation:
mygrid.attachEvent("onDrag",function(sid,tid){
    if (!some_check(sid,tid)) return false; //block d-n-d
    return true;
});
mygrid.attachEvent("onDrag",function(sid,tid){
    if (!some_check(sid,tid)) mygrid.dragContext.mode="copy"; // copy item instead of moving it
    return true;
});
The dragContext object is available during the onDrag event and it contains the following properties:
  1. dragContext.source - type of the source component ("tree","grid","treeGrid");
  2. dragContext.target - type of the target component;
  3. dragContext.sobj - the source object;
  4. dragContext.tobj - the target object;
  5. dragContext.dropmode - whether the dragged item will be a "child" or a "sibling";
  6. dragConetxt.mode - whether the dragged item will be copied or just moved: "copy" or "move";
  7. dragContext.slist() - ID(s) of the dragged item(s);
  8. dragContext.tid - ID of the drag landing item;
Any of these properties can be changed, and further dragging process will use the changed value instead of the original one.

mygrid.attachEvent("onDrag",function(sid,tid){
    some_custom_code(sid,tid);
    return false;
});
mygrid.attachEvent("onDrag",function(sid,tid){
    mygrid.moveRowTo(sid,someID,"copy","sibling"); //moving an item as a sibling of some other element
    return false; // block default d-n-d
});
Data conversion is required when d-n-d process occurs between different grids. This can be done by redefining gridToGrid method:
grid.gridToGrid = function(rowId,sgrid,tgrid){
    var z=[]; 
    for (var i=0; i<sgrid.getColumCount(); i++)    // for each cell in the source grid
        z[i]=sgrid.cells(rowId,i).getValue();         // prepare data for target grid
    return z;
}
This snippet just copies the data, without applying any modifications to it. In a real application it may change data order or add|delete some of it.

In case when d-n-d process occurs from tree to grid, the same can be done by redefining treeToGridElement or gridToTreeElement:
grid.gridToTreeElement = function(tree,treeID,gridID){
    return this.cells(gridId,0).getValue();  // take data from the first column as a value for tree
}

grid.treeToGridElement = function(tree,treeID,gridID){
    var z=[treeObj.getItemText(treeID)];  //set the tree text as a value of the first column in the grid
    return z;
}

Receiving final info about d-n-d

The last event - onDrop - occurs when an item is already placed in its final position. It can be used to catch the moment when the operation is finished.
The first parameter of onDrop event is item's ID after moving (in case of "copy" behavior it will differ from original item's ID).
This event can be used to save the data or to update some other UI based on d-n-d results.

Some details about d-n-d API

In case when d-n-d process occurs in TreeGrid it is possible to select one of the following behaviors (by setDragBehavior):

Drag as copy for all the elements can be enabled by enableMercyDrag command.